﻿#region COPYRIGHT© 2009-2013 Phillip Clark. All rights reserved.

// For licensing information see License.txt (MIT style licensing).

#endregion

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using FlitBit.Parse;

namespace FlitBit.Json
{
	/// <summary>
	/// Encapsulates a JSON array. Members are JsonValues.
	/// </summary>
	public class JsonArray : JsonValue, IList<JsonValue>
	{
		readonly List<JsonValue> _members = new List<JsonValue>();

		/// <summary>
		/// Creates a new instance.
		/// </summary>
		public JsonArray()
			: base(JsonNodeKind.Array)
		{}

		/// <summary>
		/// Gets the object's type code.
		/// </summary>
		/// <returns></returns>
		public override TypeCode GetTypeCode()
		{
			return TypeCode.Object;
		}

		/// <summary>
		/// Gets the array's string representation.
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return new JsonFormatter().Write((JsonNode) this)
																.ToString();
		}

		/// <summary>
		/// Replaces the first occurrence of a member in the array.
		/// </summary>
		/// <param name="current"></param>
		/// <param name="replacement"></param>
		void Replace(JsonValue current, JsonValue replacement)
		{
			var i = _members.IndexOf(current);
			if (i >= 0)
			{
				_members[i] = replacement;
			}
		}

		protected internal override void PerformBreadthFirstTransformFilter(Func<JsonNode, JsonNode> filter)
		{
			foreach (var m in _members.ToArray())
			{
				var n = filter(m);
				if (!ReferenceEquals(n, m))
				{
					if (n == null)
					{
						Remove(m);
					}
					else if (n is JsonValue)
					{
						Replace(m, n as JsonValue);
					}
					else
					{
						throw new InvalidOperationException("A filter returned an invalid JsonNode, the member cannot be replaced.");
					}
				}
				else
				{
					m.PerformBreadthFirstTransformFilter(filter);
				}
			}
		}

		protected internal override void PerformDepthFirstTransformFilter(Func<JsonNode, JsonNode> filter)
		{
			foreach (var m in _members.ToArray())
			{
				m.PerformDepthFirstTransformFilter(filter);

				var n = filter(m);
				if (!ReferenceEquals(n, m))
				{
					if (n == null)
					{
						Remove(m);
					}
					else if (n is JsonValue)
					{
						Replace(m, n as JsonValue);
					}
					else
					{
						throw new InvalidOperationException("A filter returned an invalid JsonNode, the member cannot be replaced.");
					}
				}
			}
		}

		protected internal override void WriteFormatted(JsonFormatter formatter)
		{
			formatter.OpenScope("[");
			formatter.WriteMembers(from item in _members
														select item);
			formatter.CloseScope("]");
		}

		#region IList<JsonValue> Members

		public int IndexOf(JsonValue item)
		{
			return _members.IndexOf(item);
		}

		public void Insert(int index, JsonValue item)
		{
			_members.Insert(index, item);
		}

		public void RemoveAt(int index)
		{
			_members.RemoveAt(index);
		}

		public JsonValue this[int index] { get { return _members[index]; } set { _members[index] = value; } }

		public void Add(JsonValue item)
		{
			_members.Add(item);
		}

		public void Clear()
		{
			_members.Clear();
		}

		public bool Contains(JsonValue item)
		{
			return _members.Contains(item);
		}

		public void CopyTo(JsonValue[] array, int arrayIndex)
		{
			_members.CopyTo(array, arrayIndex);
		}

		public int Count { get { return _members.Count; } }

		public bool IsReadOnly { get { return false; } }

		public bool Remove(JsonValue item)
		{
			return _members.Remove(item);
		}

		public IEnumerator<JsonValue> GetEnumerator()
		{
			return _members.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		#endregion

		internal static JsonArray ExpectArray(TokenizerState<JsonTokenKind> tokens)
		{
			tokens.ExpectTake(JsonTokenKind.Left_Brace_Symbol);
			var arr = new JsonArray();
			var look = tokens.ExpectAtAny(
																	 JsonTokenKind.Left_Brace_Symbol,
																	JsonTokenKind.Left_Curly_Brace_Symbol,
																	JsonTokenKind.StringLiteral,
																	JsonTokenKind.NumericLiteral,
																	JsonTokenKind.True,
																	JsonTokenKind.False,
																	JsonTokenKind.Null,
																	JsonTokenKind.Right_Brace_Symbol);
			if (look.Kind == JsonTokenKind.Right_Brace_Symbol)
			{
				tokens.MoveNext();
			}
			else
			{
				while (look.Kind != JsonTokenKind.Right_Brace_Symbol)
				{
					arr.Add(ExpectValue(tokens));

					look = tokens.ExpectTakeAny(JsonTokenKind.Comma_Symbol, JsonTokenKind.Right_Brace_Symbol);
				}
			}
			return arr;
		}
	}
}